//
//  ARScanningAnimations.swift
//  ARScanningDemo
//
//  Created by wanglc on 2018/3/8.
//  Copyright © 2018年 GitHub. All rights reserved.
//

import UIKit

struct ARScanningScale {
    var fromRate: Float = 0.0
    var toRate: Float = 0.0
    var duration: TimeInterval = 0.0
}

struct ARScanningAlpha {
    var fromAlpha: Float = 0.0
    var toAlpha: Float = 0.0
    var duration: TimeInterval = 0.0
}

struct ARScanningRotation {
    var clockwise: Bool = true
    var speedTime: TimeInterval = 0.0
    var beginTime: TimeInterval = 0.0
    var repeatCount: Float = MAXFLOAT
}

struct ARScanningDegree {
    var fromDegree: CGFloat = 0.0
    var toDegree: CGFloat = 0.0
    var duration: TimeInterval = 0.0
}

struct ARScanningColor {
    var fromColor: UIColor
    var toColor: UIColor
    var duration: TimeInterval = 0.0
}

struct ARScanningRadius {
    var fromRadius: Float = 0.0
    var toRadius: Float = 0.0
    var duration: TimeInterval = 0.0
}

class ARScanningAnimations: NSObject {

    var parentLayer: CALayer?
    
    //开始扫描动画
    func addScanningAnimationsTo(layer: CALayer, delayTime: TimeInterval, animationKey: String) -> Void {
        
        var groups = Array<CABasicAnimation>()
        
        if (animationKey == "BlueLayerScanning") || (animationKey == "OutLayerScanning") || (animationKey == "InLayerScanning") {
            
            let scales = [ARScanningScale(fromRate: 0.0, toRate: 1.04, duration: 0.2),
                          ARScanningScale(fromRate: 1.04, toRate: 1.0, duration: 0.066),
                          ARScanningScale(fromRate: 1.0, toRate: 1.02, duration: 0.066),
                          ARScanningScale(fromRate: 1.02, toRate: 1.0, duration: 0.066)]
            groups += addScaleAnimations(scales: scales, delayTime: delayTime)
            
            let alphas = [ARScanningAlpha(fromAlpha: 0.0, toAlpha: 1.0, duration: 0.2)]
            groups += addAlphaAnimations(alphas: alphas, delayTime: delayTime)
            
            var degree: CGFloat = 0.0
            if (animationKey == "BlueLayerScanning") {
                degree = 360
            }else if (animationKey == "outLayerScanning") {
                degree = -180
            }else {
                degree = -90
            }
            let degrees = [ARScanningDegree(fromDegree: 0.0, toDegree: degree, duration: 0.866)]
            groups += addRotationDegreeAnimations(degrees: degrees, delayTime: 0.8)
            
            /*
            var speedTime: TimeInterval = 0.866
            var isClockwise = false
            if (animationKey == "BlueLayerScanning") {
                isClockwise = true
            }else if (animationKey == "outLayerScanning") {
                speedTime = 2*speedTime
            }else {
                speedTime = 4*speedTime
            }
            let rotations = [ARScanningRotation(clockwise: isClockwise, speedTime: speedTime, beginTime: 0.8, repeatCount: MAXFLOAT)]
            groups += addRotationAnimations(rotations: rotations)
             */
        }else {
            
            let alphas = [ARScanningAlpha(fromAlpha: 0.0, toAlpha: 1.0, duration: 0.2)]
            groups += addAlphaAnimations(alphas: alphas, delayTime: delayTime)
            
            if (animationKey == "CenterCircleLayerScanning") {
                
                let rotations = [ARScanningRotation(clockwise: true, speedTime: 8.0, beginTime: 0.0, repeatCount: MAXFLOAT)]
                groups += addRotationAnimations(rotations: rotations)
            }
        }
        
       addGroupAniamtionsTo(layer: layer, groups: groups, animationKey: animationKey, duration: 1.666)
    }
    
    //扫描完成动画
    func addScanningEndAnimations(layer: CALayer, animationKey: String) -> Void {
        
        layer.removeAllAnimations()
        
        var groups = Array<CABasicAnimation>()
        
        //缩放
        let scales = [ARScanningScale(fromRate: 1.0, toRate: 0.9, duration: 0.334)]
        groups += addScaleAnimations(scales: scales, delayTime: 0.0)
        
        //淡入淡出
        var alpha: Float = 1.0
        if (animationKey == "BlueLayerScanningEnd") || (animationKey == "InLayerScanningEnd") {
            alpha = 0.8
        }else if (animationKey == "OutLayerScanningEnd") || (animationKey == "WhiteCircleLayerScanningEnd")  {
            alpha = 0.0
        }
        let alphas = [ARScanningAlpha(fromAlpha: 1.0, toAlpha: alpha, duration: 0.866)]
        groups += addAlphaAnimations(alphas: alphas, delayTime: 1.134)
        
        //旋转
        if (animationKey == "BlueLayerScanningEnd") || (animationKey == "OutLayerScanningEnd") || (animationKey == "InLayerScanningEnd") {
            
            var lastSpeed: TimeInterval = 12.0
            var isClockwise = true
            if (animationKey != "BlueLayerScanningEnd") {
                lastSpeed = 6.0
                isClockwise = false
            }
            let rotations = [ARScanningRotation(clockwise: isClockwise, speedTime: 0.334, beginTime: 0.0, repeatCount: 1.0),
                             ARScanningRotation(clockwise: isClockwise, speedTime: 0.0, beginTime: 0.334, repeatCount: 1.0),
                             ARScanningRotation(clockwise: !isClockwise, speedTime: lastSpeed, beginTime: 1.134, repeatCount: MAXFLOAT)]
            groups += addRotationAnimations(rotations: rotations)
        }
        
        if (animationKey == "CenterCircleLayerScanningEnd") {
            let rotations = [ARScanningRotation(clockwise: false, speedTime: 8.0, beginTime: 1.134, repeatCount: MAXFLOAT)]
            groups += addRotationAnimations(rotations: rotations)
        }
        
        //颜色和阴影
        if (animationKey == "BlueLayerScanningEnd") {
            addBlueGradientLayerColorAnimations(layer: layer, delayTime: 0.0)
            addBlueArcLayerShadowAnimations(layer: layer,  delayTime: 0.2)
        }
        
        addGroupAniamtionsTo(layer: layer, groups: groups, animationKey: animationKey, duration: TimeInterval(MAXFLOAT))
    }
    
    func addAlertLayerAnimations(pointLayer: CALayer, polylineLayer: CAShapeLayer, textLayer: CATextLayer, pinyinLayer: CATextLayer) -> Void {
        
        let stroke = self.strokeLine(duration: 2.0, beginTime: 0.0)
        polylineLayer.add(stroke, forKey: "")
        
        let alpha1 = self.alpha(fromAlpha: 0.0, toAlpha: 1.0, duration: 2.0, beginTime: 0.334)
        pointLayer.add(alpha1, forKey: "")
        
        let alpha2 = self.alpha(fromAlpha: 0.0, toAlpha: 1.0, duration: 4.0, beginTime: 0.0)
        textLayer.add(alpha2, forKey: "")
        pinyinLayer.add(alpha2, forKey: "")
    }
    
    //MARK: 工厂模式
    
    private func addScaleAnimations(scales: Array<ARScanningScale>, delayTime: TimeInterval) -> Array<CABasicAnimation> {
        
        var animationsTime: TimeInterval = delayTime
        var array = Array<CABasicAnimation>()
        
        for scale in scales {
            let animation = self.scaleRate(fromRate: scale.fromRate, toRate: scale.toRate, duration: scale.duration, beginTime: animationsTime)
            array.append(animation)
            
            animationsTime += scale.duration
        }
        
        return array
    }
    
    private func addAlphaAnimations(alphas: Array<ARScanningAlpha>, delayTime: TimeInterval) -> Array<CABasicAnimation>  {
        
        var animationsTime: TimeInterval = delayTime
        var array = Array<CABasicAnimation>()
        
        for alpha in alphas {
            
            let animation = self.alpha(fromAlpha: alpha.fromAlpha, toAlpha: alpha.toAlpha, duration: alpha.duration, beginTime: animationsTime)
            array.append(animation)
            
            animationsTime += alpha.duration
        }
        
        return array
    }
    
    private func addRotationAnimations(rotations: Array<ARScanningRotation>) -> Array<CABasicAnimation> {
        
        var array = Array<CABasicAnimation>()
        
        for rotation in rotations {
            
            let animation = self.rotation(speedTime: rotation.speedTime, clockwise: rotation.clockwise, beginTime: rotation.beginTime, repeatCount: rotation.repeatCount)
            array.append(animation)
        }
        
        return array
    }
    
    private func addRotationDegreeAnimations(degrees: Array<ARScanningDegree>, delayTime: TimeInterval) -> Array<CABasicAnimation> {
        
        var animationsTime: TimeInterval = delayTime
        var array = Array<CABasicAnimation>()
        
        for degree in degrees {
            
            let animation = self.rotationToDegree(fromDegree: degree.fromDegree, toDegree: degree.toDegree, duration: degree.duration, beginTime: animationsTime)
            array.append(animation)
            
            animationsTime += degree.duration
        }
        
        return array
    }
    
    private func addColorAnimations(colors: Array<ARScanningColor>, delayTime: TimeInterval, keyPath: String) -> Array<CABasicAnimation> {
        
        var animationsTime: TimeInterval = delayTime
        var array = Array<CABasicAnimation>()
        
        for color in colors {
            
             let animation = self.color(fromColor: color.fromColor, toColor: color.toColor, duration: color.duration, beginTime: animationsTime, keyPath: keyPath)
            array.append(animation)
            
            animationsTime += color.duration
        }
        
        return array
    }
    
    private func addBlueGradientLayerColorAnimations(layer: CALayer, delayTime: TimeInterval) -> Void {
        
        let sublayers = layer.sublayers
        
        var index: Int = 0
        for sublayer in sublayers! {
            
            if sublayer is CAGradientLayer {
                
                if index > 1 {
                    return
                }
                //渐变色
                let gradientLayer = sublayer as! CAGradientLayer
                let animation = self.gradientColor(toColors: [UIColor.white.cgColor, UIColor.white.cgColor], duration: 0.334, beginTime: delayTime)
                gradientLayer.add(animation, forKey: "GradientLayerColor")
                
                index += 1
            }
        }
        
        layer.needsDisplay()
    }
    
    private func addBlueArcLayerShadowAnimations(layer: CALayer,  delayTime: TimeInterval) -> Void {
        
        var groups = Array<CABasicAnimation>()
        
        let sublayers = layer.sublayers
        for sublayer in sublayers! {
            
            var index: Int = 0
            if sublayer is CAGradientLayer {
                
                if index > 1 {
                    return
                }
                
                let shapeLayer = sublayer.mask as! CAShapeLayer
                //阴影
                let shadows = [ARScanningRadius(fromRadius: 0, toRadius: 5.0, duration: 0.134),
                               ARScanningRadius(fromRadius: 5.0, toRadius: 0.0, duration: 0.4)]
                groups += addShadowRadiusAnimations(radiuses: shadows, delayTime: delayTime)
                addGroupAniamtionsTo(layer: shapeLayer, groups: groups, animationKey: "BlueArcLayerShadow", duration: 2.4)
                
                index += 1
            }
        }
        
        layer.needsDisplay()
    }
    
    private func addShadowRadiusAnimations(radiuses: Array<ARScanningRadius>, delayTime: TimeInterval) -> Array<CABasicAnimation>  {
        
        var animationsTime: TimeInterval = delayTime
        var array = Array<CABasicAnimation>()
        
        for radius in radiuses {
            
            let animation = self.shadowRadius(fromRadius: radius.fromRadius, toRadius: radius.toRadius, duration: radius.duration, beginTime: delayTime)
            array.append(animation)
            
            animationsTime += radius.duration
        }
        
        return array
    }
    
    //MARK: 基础动画
    
    private func addGroupAniamtionsTo(layer: CALayer, groups: Array<CABasicAnimation>,animationKey: String, duration: TimeInterval) -> Void {
        
        let group = CAAnimationGroup()
        group.animations = groups
        group.duration = duration
        group.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        group.isRemovedOnCompletion = false
        group.fillMode = kCAFillModeForwards
        
        layer.add(group, forKey: animationKey)
    }
    
    /// 旋转
    ///
    /// - Parameters:
    ///   - clockwise: 是否顺时针旋转
    ///   - speedTime: 旋转一圈的动画时长
    /// - Returns: Self
    private func rotation(speedTime: TimeInterval, clockwise: Bool, beginTime: TimeInterval, repeatCount: Float) -> CABasicAnimation {
        
        let animation = CABasicAnimation()
        animation.keyPath = "transform.rotation.z"
        if clockwise {
            animation.fromValue = 0.0
            animation.toValue = (CGFloat).pi*2
        }else {
            animation.fromValue = 0.0
            animation.toValue = -(CGFloat).pi*2
        }
        animation.duration = speedTime
        animation.autoreverses = false
        animation.isRemovedOnCompletion = false
        animation.fillMode = kCAFillModeForwards
        animation.repeatCount = repeatCount
        animation.beginTime = beginTime
        
        return animation
    }
    
    private func rotationToDegree(fromDegree: CGFloat, toDegree: CGFloat, duration: TimeInterval, beginTime: TimeInterval) -> CABasicAnimation {
        
        let animation = CABasicAnimation()
        animation.keyPath = "transform.rotation.z"
        animation.fromValue = fromDegree.toAngle()
        animation.toValue = toDegree.toAngle()
        animation.duration = duration
        animation.autoreverses = false
        animation.isRemovedOnCompletion = false
        animation.fillMode = kCAFillModeForwards
        animation.repeatCount = 1.0
        animation.beginTime = beginTime
        
        return animation
    }
    
    /// 比例缩放
    ///
    /// - Parameters:
    ///   - rate: 缩放比例
    ///   - duration: 动画时长
    /// - Returns: Self
    private func scaleRate(fromRate: Float, toRate: Float, duration: TimeInterval, beginTime: TimeInterval) -> CABasicAnimation {
        
        let animation = CABasicAnimation()
        animation.keyPath = "transform.scale"
        animation.fromValue = fromRate
        animation.toValue = toRate
        animation.duration = duration
        animation.autoreverses = false
        animation.isRemovedOnCompletion = false
        animation.fillMode = kCAFillModeForwards
        animation.beginTime = beginTime
        
        return animation
    }
    
    /// 淡入淡出
    ///
    /// - Parameters:
    ///   - alpha: 透明度
    ///   - duration: 动画时长
    /// - Returns: Self
    private func alpha(fromAlpha: Float, toAlpha: Float, duration: TimeInterval, beginTime: TimeInterval) -> CABasicAnimation {
        
        let animation = CABasicAnimation()
        animation.keyPath = "opacity"
        animation.fromValue = fromAlpha
        animation.toValue = toAlpha
        animation.duration = duration
        animation.autoreverses = false
        animation.isRemovedOnCompletion = false
        animation.fillMode = kCAFillModeForwards
        animation.beginTime = beginTime
        
        return animation
    }
    
    /// 颜色变化
    ///
    /// - Parameters:
    ///   - color: 颜色
    ///   - duration: 动画时长
    /// - Returns: Self
    private func color(fromColor: UIColor, toColor: UIColor, duration: TimeInterval, beginTime: TimeInterval, keyPath: String) -> CABasicAnimation {
        
        let animation = CABasicAnimation()
        animation.keyPath = keyPath
        animation.fromValue = fromColor.cgColor
        animation.toValue = toColor.cgColor
        animation.duration = duration
        animation.autoreverses = false
        animation.isRemovedOnCompletion = false
        animation.fillMode = kCAFillModeForwards
        animation.beginTime = beginTime
        
        return animation
    }
    
    private func gradientColor(toColors: Array<CGColor>, duration: TimeInterval, beginTime: TimeInterval) -> CABasicAnimation {
        
        let animation = CABasicAnimation()
        animation.keyPath = "colors"
        animation.toValue = toColors
        animation.duration = duration
        animation.autoreverses = false
        animation.isRemovedOnCompletion = false
        animation.fillMode = kCAFillModeForwards
        animation.beginTime = beginTime
        
        return animation
    }
    
    //阴影
    private func shadowRadius(fromRadius: Float, toRadius: Float, duration: TimeInterval, beginTime: TimeInterval) -> CABasicAnimation {
        
        let animation = CABasicAnimation()
        animation.keyPath = "shadowRadius"
        animation.fromValue = fromRadius
        animation.toValue = toRadius
        animation.duration = duration
        animation.autoreverses = false
        animation.isRemovedOnCompletion = false
        animation.fillMode = kCAFillModeForwards
        animation.beginTime = beginTime
        
        return animation
    }
    
    //折线
    private func strokeLine(duration: TimeInterval, beginTime: TimeInterval) -> CABasicAnimation {
        
        let animation = CABasicAnimation()
        animation.keyPath = "strokeEnd"
        animation.fromValue = Float(0.0)
        animation.toValue = Float(1.0)
        animation.duration = duration
        animation.autoreverses = false
        animation.isRemovedOnCompletion = false
        animation.fillMode = kCAFillModeForwards
        animation.beginTime = beginTime
        
        return animation
    }
}
